 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.decorators;

 import org.eclipse.swt.graphics.Device;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.graphics.PaletteData;
 import org.eclipse.swt.graphics.RGB;

 /**
  * DecorationImageBuilder is a utility class for merging images without data
  * loss.
  *
  * @since 3.3
  *
  */
 class DecorationImageBuilder {

     private static final int TOP_LEFT = LightweightDecoratorDefinition.TOP_LEFT;
     private static final int TOP_RIGHT = LightweightDecoratorDefinition.TOP_RIGHT;
     private static final int BOTTOM_LEFT = LightweightDecoratorDefinition.BOTTOM_LEFT;
     private static final int BOTTOM_RIGHT = LightweightDecoratorDefinition.BOTTOM_RIGHT;
     private static final int UNDERLAY = LightweightDecoratorDefinition.UNDERLAY;

     private static final PaletteData ALPHA_PALETTE, BW_PALETTE;
     static {
         RGB[] rgbs = new RGB[256];
         for (int i = 0; i < rgbs.length; i++) {
             rgbs[i] = new RGB(i, i, i);
         }
         ALPHA_PALETTE = new PaletteData(rgbs);
         BW_PALETTE = new PaletteData(new RGB[] { new RGB(0, 0, 0),
                 new RGB(255, 255, 255) });
     }

     private static int getTransparencyDepth(ImageData data) {
         if (data.maskData != null && data.depth == 32) {
             for (int i = 0; i < data.data.length; i += 4) {
                 if (data.data[i] != 0)
                     return 8;
             }
         }
         if (data.maskData != null || data.transparentPixel != -1)
             return 1;
         if (data.alpha != -1 || data.alphaData != null)
             return 8;
         return 0;
     }

     private static ImageData getTransparency(ImageData data,
             int transparencyDepth) {
         if (data == null)
             return null;
         if (transparencyDepth == 1)
             return data.getTransparencyMask();
         ImageData mask = null;
         if (data.maskData != null && data.depth == 32) {
             ImageData m = data.getTransparencyMask();
             mask = new ImageData(data.width, data.height, 8, ALPHA_PALETTE,
                     data.width, new byte[data.width * data.height]);
             for (int y = 0; y < data.height; y++) {
                 for (int x = 0; x < data.width; x++) {
                     int alpha = data.getPixel(x, y) & 0xFF;
                     if (alpha == 0) {
                         if (m.getPixel(x, y) != 0)
                             alpha = 255;
                     }
                     mask.setPixel(x, y, alpha);
                 }
             }
         } else if (data.maskData != null || data.transparentPixel != -1) {
             ImageData m = data.getTransparencyMask();
             mask = new ImageData(data.width, data.height, 8, ALPHA_PALETTE,
                     data.width, new byte[data.width * data.height]);
             for (int y = 0; y < mask.height; y++) {
                 for (int x = 0; x < mask.width; x++) {
                     mask.setPixel(x, y, m.getPixel(x, y) != 0 ? (byte) 255 : 0);
                 }
             }
         } else if (data.alpha != -1) {
             mask = new ImageData(data.width, data.height, 8, ALPHA_PALETTE,
                     data.width, new byte[data.width * data.height]);
             for (int i = 0; i < mask.data.length; i++) {
                 mask.data[i] = (byte) data.alpha;
             }
         } else if (data.alphaData != null) {
             mask = new ImageData(data.width, data.height, 8, ALPHA_PALETTE,
                     data.width, data.alphaData);
         } else {
             mask = new ImageData(data.width, data.height, 8, ALPHA_PALETTE,
                     data.width, new byte[data.width * data.height]);
             for (int i = 0; i < mask.data.length; i++) {
                 mask.data[i] = (byte) 255;
             }
         }
         return mask;
     }

     private static void composite(ImageData dst, ImageData src, int xOffset,
             int yOffset) {
         if (dst.depth == 1) {
             for (int y = 0, dstY = y + yOffset; y < src.height; y++, dstY++) {
                 for (int x = 0, dstX = x + xOffset; x < src.width; x++, dstX++) {
                     if (0 <= dstX && dstX < dst.width && 0 <= dstY
                             && dstY < dst.height) {
                         if (src.getPixel(x, y) != 0) {
                             dst.setPixel(dstX, dstY, 1);
                         }
                     }
                 }
             }
         } else if (dst.depth == 8) {
             for (int y = 0, dstY = y + yOffset; y < src.height; y++, dstY++) {
                 for (int x = 0, dstX = x + xOffset; x < src.width; x++, dstX++) {
                     if (0 <= dstX && dstX < dst.width && 0 <= dstY
                             && dstY < dst.height) {
                         int srcAlpha = src.getPixel(x, y);
                         int dstAlpha = dst.getPixel(dstX, dstY);
                         dstAlpha += (srcAlpha - dstAlpha) * srcAlpha / 255;
                         dst.setPixel(dstX, dstY, dstAlpha);
                     }
                 }
             }
         }
     }

     /**
      * Create a composite image by underlaying and overlaying the base image.
      * @param device
      * @param base
      * @param overlay
      * @return Image
      */
     static Image compositeImage(Device device, ImageData base,
             ImageData[] overlay) {
         if (base == null)
             return null;
         Image image = new Image(device, new ImageData(base.width, base.height,
                 24, new PaletteData(0xff, 0xff00, 0xff00000)));
         GC gc = new GC(image);
         ImageData src;
         int maskDepth = 0, baseMaskDepth = 0;
         ImageData underlay = src = overlay.length > UNDERLAY ? overlay[UNDERLAY]
                 : null;
         if (src != null) {
             maskDepth = Math.max(maskDepth, getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, 0, 0);
             img.dispose();
         }
         src = base;
         if (base != null) {
             maskDepth = Math.max(maskDepth,
                     baseMaskDepth = getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, 0, 0);
             img.dispose();
         }
         ImageData topLeft = src = overlay[TOP_LEFT];
         if (src != null) {
             maskDepth = Math.max(maskDepth, getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, 0, 0);
             img.dispose();
         }
         ImageData topRight = src = overlay[TOP_RIGHT];
         if (src != null) {
             maskDepth = Math.max(maskDepth, getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, base.width - src.width, 0);
             img.dispose();
         }
         ImageData bottomLeft = src = overlay[BOTTOM_LEFT];
         if (src != null) {
             maskDepth = Math.max(maskDepth, getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, 0, base.height - src.height);
             img.dispose();
         }
         ImageData bottomRight = src = overlay[BOTTOM_RIGHT];
         if (src != null) {
             maskDepth = Math.max(maskDepth, getTransparencyDepth(src));
             Image img = new Image(device, src);
             gc.drawImage(img, base.width - src.width, base.height - src.height);
             img.dispose();
         }
         gc.dispose();
         if (baseMaskDepth > 0) {
             ImageData newData = image.getImageData();
             image.dispose();
             ImageData mask = null;
             switch (maskDepth) {
             case 1:
                 mask = new ImageData(base.width, base.height, maskDepth,
                         BW_PALETTE);
                 break;
             case 8:
                 mask = new ImageData(base.width, base.height, maskDepth,
                         ALPHA_PALETTE, base.width, new byte[base.width
                                 * base.height]);
                 break;
             }
             src = getTransparency(underlay, maskDepth);
             if (src != null)
                 composite(mask, src, 0, 0);
             src = getTransparency(base, maskDepth);
             if (src != null)
                 composite(mask, src, 0, 0);
             src = getTransparency(topLeft, maskDepth);
             if (src != null)
                 composite(mask, src, 0, 0);
             src = getTransparency(topRight, maskDepth);
             if (src != null)
                 composite(mask, src, mask.width - src.width, 0);
             src = getTransparency(bottomLeft, maskDepth);
             if (src != null)
                 composite(mask, src, 0, mask.height - src.height);
             src = getTransparency(bottomRight, maskDepth);
             if (src != null)
                 composite(mask, src, mask.width - src.width, mask.height
                         - src.height);
             switch (maskDepth) {
             case 1:
                 newData.maskData = mask.data;
                 newData.maskPad = mask.scanlinePad;
                 break;
             case 8:
                 newData.alphaData = mask.data;
                 break;
             }
             image = new Image(device, newData);
         }
         return image;
     }

 }
